package controllers

import java.io.File
import java.nio.file.Files
import akka.stream.IOResult
import akka.stream.scaladsl.FileIO
import config.{Global, MyAwait, MyFile, MyRow, MyString}
import dao.GenomeDao
import play.api.data.Form
import play.api.data.Forms._

import javax.inject.Inject
import play.api.libs.json.Json
import play.api.libs.streams.Accumulator
import play.api.mvc.MultipartFormData.FilePart
import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents}
import play.core.parsers.Multipart.{FileInfo, FilePartHandler}
import tools._
import utils.TableUtils

import scala.concurrent.{ExecutionContext, Future}
import scala.io.Source
import scala.math.log10
import scala.util.Failure

class ToolsController @Inject()(genomeDao:GenomeDao,cc: ControllerComponents)
                               (implicit exec: ExecutionContext) extends AbstractController(cc) with MyFile with MyRow with MyAwait with MyString{


  def toolPage = Action { implicit request =>
    Ok(views.html.tools.toolsPage())
  }

  def toolsPage(page: String) = Action { implicit request =>
    Ok(
      page match {
        case "primerPage" => views.html.tools.primerPage()
        case "musclePage" => views.html.tools.musclePage()
        case "lastzPage" => views.html.tools.lastzPage()
        case "genewisePage" => views.html.tools.genewisePage()
        case "goPage" => views.html.tools.goPage()
        case "keggPage" => views.html.tools.keggPage()
        case "singleLocusPage" => views.html.tools.singleLocusPage()
      //  case "snpHeatmapPage" => views.html.tools.snpHeatmapPage()
      //  case "geneExpressionPage" => views.html.tools.geneExpressionPage()

      }
    )
  }

  private def handleFilePartAsFile: FilePartHandler[File] = {
    case FileInfo(partName, filename, contentType, dispositionType) =>
      val file = new File(Global.tmpPath, Global.random)
      val path = file.toPath
      Accumulator(FileIO.toPath(path)).mapFuture {
        case IOResult(_, Failure(error)) => Future.failed(error)
        case IOResult(count, _) =>
          Future.successful(FilePart(partName, filename, contentType, file, count, dispositionType))
      }
  }


  def toolsRun(tools: String): Action[AnyContent] = Action { implicit request =>
    Ok(
      tools match {
        case "genewise" => GeneWise.genewise
        case "kegg" => GoKegg.goOrKegg("kegg")
        case "go" => GoKegg.goOrKegg("go")
        case "primer" => Primer3.PrimerRun
      }
    )
  }

  def toolsFileRun(tools: String) = Action(parse.multipartFormData(handleFilePartAsFile)) { implicit request =>
    Ok(
      tools match {
        case "muscle" => Muscle.muscle
        case "lastz" => Lastz.lastz
      }
    )
  }

  case class RangeData(chr: String, start: Int, end: Int)

  val RangeForm = Form(
    mapping(
      "chr" -> text,
      "start" -> number,
      "end" -> number
    )(RangeData.apply)(RangeData.unapply)
  )

  def snpHeatmapRun = Action { implicit request =>
    val path = if (Global.isWindow) {
      "H:\\南农油茶数据库\\2024_4_8"
    } else {
      s"${Global.path}/config"
    }

    val data = RangeForm.bindFromRequest.get

    val chr = s"$path/snp_freq/${data.chr}.txt"
    val chrIndex = s"$path/snp_freq_index/${data.chr}.txt".readLines
    val lines = chrIndex.map(_.toInt).zipWithIndex.filter(x => x._1 >= data.start && x._1 <= data.end).map(_._2).take(2000)

    if (lines.nonEmpty) {

      val result = Source.fromFile(chr.toFile).getLines().slice(lines.head, lines.last).toSeq.map(_.split("\t"))
      val header = result.flatMap { x =>
        x.drop(4).map(_.split(":").head)
      }.distinct

      val freqMap = result.map { x =>
        val pos = x(1)
        val freq = x.drop(4).map(_.split(":")).map(y => y.head -> y.last).toMap
        Map("name" -> pos) ++ freq
      }

      val categories = result.map(_(1))

      val series = header.map { x =>
        Json.obj("name" -> x, "data" -> freqMap.map(y => y.getOrElse(x, "0").toDouble))
      }

      Ok(Json.obj("categories" -> categories, "series" -> series))

    } else {
      Ok(Json.obj("valid" -> "false", "msg" -> "No SNP information in the interval!"))
    }

  }

  case class ExprData(ids: String)

  val ExprForm = Form(
    mapping(
      "ids" -> text
    )(ExprData.apply)(ExprData.unapply)
  )

  case class ExprRangeData(chr: String, start: Int, end: Int)

  val ExprRangeForm = Form(
    mapping(
      "chr" -> text,
      "start" -> number,
      "end" -> number
    )(ExprRangeData.apply)(ExprRangeData.unapply)
  )

  def getGeneExprData(formId: String) = Action { implicit request =>
    val geneids = formId match {
      case "geneIdForm" => ExprForm.bindFromRequest.get.ids.trim.split(",").map(_.trim).toSeq
      case "rangeForm" =>
        val data = ExprRangeForm.bindFromRequest.get
        genomeDao.getByRange("CON", data.chr, data.start, data.end).toAwait.map(_.geneid).toSeq
    }
    val rna = geneids.map { x =>
      x -> TableUtils.fpkm.get(x)
    }.filter(_._2.nonEmpty).map { x =>
      val geneid = x._1
      val data =x._2.get
      geneid -> data.split("\t").map(_.toDouble)
    }

    val table = rna.map { x => (x._1 +: x._2.map(_.toString)).zipWithIndex.map { y => y._2.toString -> y._1 }.toMap }


    val header = "Gene ID" +:  TableUtils.fpkmHeader

    val heatmap = {
      val data = "GeneID,mRNA,EXP\n" + rna.zipWithIndex.flatMap { x =>
        x._1._2.zipWithIndex.map { y =>
          y._2 + "," + x._2 + "," + log2(y._1)
        }
      }.mkString("\n")
      val xAxis = header.tail
      val yAxis = rna.map(_._1)
      Json.obj("xAxis" -> xAxis, "yAxis" -> yAxis, "csv" -> data)
    }

    val line = {
      val data = rna.map { x =>
        Json.obj("name" -> x._1, "data" -> x._2)
      }
      val xAxis = header.tail

      Json.obj("xAxis" -> xAxis, "series" -> data)

    }

    Ok(Json.obj("table" -> table, "header" -> header, "heatmap" -> heatmap, "line" -> line))
  }

  def log2(x: Double): Double = log10(x + 1) / log10(2.0)
}
